package scan
{
	import flash.utils.Dictionary;

	/**
	 * Group of frames
	 */
	public class Group extends Space
	{
		////////////////////////////////////////////////////
		// 
		////////////////////////////////////////////////////
		
		public static const WIDTH_LIMIT:int = 2048;
		public static const HEIGHT_LIMIT:int = 2048;
		
		////////////////////////////////////////////////////
		// 
		////////////////////////////////////////////////////
		
		private var _frames:Vector.<IFrame>;
		private var _spaceMatrix:Vector.<Vector.<Space>>;
		private var _smWidth:int = 0;
		private var _smHeight:int = 0;
		
		////////////////////////////////////////////////////
		// 
		////////////////////////////////////////////////////
		
		public function Group(frames:Vector.<IFrame>=null)
		{
			super();
			
			var w:int = calcWidth(frames);
			var h:int = calcHeight(frames);
			
			if (w > WIDTH_LIMIT || h > HEIGHT_LIMIT)
				throw Error("The total size of of frames is more than acceptable limits.");
			
			if (!frames)
			{
				_frames = new Vector.<IFrame>();
			}
			else
			{
				_frames = frames;
				
				width = w;
				height = h;
			}
			
			scanForSpaces();
		}
		
		public function get spaces():Vector.<Space>
		{
			var res:Vector.<Space> = new Vector.<Space>();
			for (var i:int = 0; i < _spaceMatrix.length; i++)
			{
				var row:Vector.<Space> = _spaceMatrix[i];
				for (var j:int = 0; j < row.length; j++)
				{
					var s:Space = row[j];
					if (s)
						res.push(s);
				}
			}
			return res;
		}
		
		public function getSpaceForFrame(frame:IFrame):Space
		{
			for (var i:int = 0; i < _spaceMatrix.length; i++)
			{
				var row:Vector.<Space> = _spaceMatrix[i];
				for (var j:int = 0; j < row.length; j++)
				{
					var s:Space = expand(row[j], frame, j, i);
					if (s)
						return s;
				}
			}
			return null;
		}
		
		private function expand(space:Space, frame:IFrame, mx:int, my:int):Space
		{
			if (!space)
				return null;
			
			var res:Space = space.clone();
			var curX:int = mx;
			var curY:int = my;
			
			while (!res.fit(frame))
			{
				if (frame.width > res.width)
				{
					curX++;
					
					if (curX >= _smWidth)
						return null;
					
					var s:Space = _spaceMatrix[my][curX];
					if (!s)
						return null;
					
					res.width += s.width;
				}
				else
				{
					curY++;
					
					if (curY >= _smHeight)
						return null;
					
					for (var i:int = mx; i <= curX; i++)
					{
						s = _spaceMatrix[curY][i];
						if (!s)
							return null;
					}
					
					res.height += s.height;
				}
			}
			
			return res;
		}
		
		public function insert(frame:IFrame, space:Space=null):Boolean
		{
			if (space)
			{
				if (space.width < frame.width || space.height < frame.height)
					throw Error("Wrong space for frame.");
				
				frame.x = space.x;
				frame.y = space.y;
				
				_frames.push(frame);
			}
			else
			{
				if (frame.isPortrait)
				{
					if (width + frame.width > WIDTH_LIMIT)
					{
						if (height + frame.height > HEIGHT_LIMIT)
							return false;
						
						frame.x = 0;
						frame.y = height;
						
						_frames.push(frame);
					}
					else
					{
						frame.x = width;
						frame.y = 0;
						
						_frames.push(frame);
					}
				}
				else
				{
					if (height + frame.height > HEIGHT_LIMIT)
					{
						if (width + frame.width > WIDTH_LIMIT)
							return false;
						
						frame.x = width;
						frame.y = 0;
						
						_frames.push(frame);
					}
					else
					{
						frame.x = 0;
						frame.y = height;
						
						_frames.push(frame);
					}
				}
			}
			
			width = calcWidth();
			height = calcHeight();
			
			scanForSpaces();
			return true;
		}
		
		////////////////////////////////////////////////////
		// 
		////////////////////////////////////////////////////
		
		private function calcWidth(frames:Vector.<IFrame>=null):int
		{
			if (!frames) frames = _frames;
			
			var res:int = 0;
			for each (var f:IFrame in frames)
			{
				var r:int = f.x + f.width;
				if (res < r) res = r;
			}
			return res;
		}
		
		private function calcHeight(frames:Vector.<IFrame>=null):int
		{
			if (!frames) frames = _frames;
			
			var res:int = 0;
			for each (var f:IFrame in frames)
			{
				var b:int = f.y + f.height;
				if (res < b) res = b;
			}
			return res;
		}
		
		private function scanForSpaces():void
		{
			_spaceMatrix = new Vector.<Vector.<Space>>();
			_smWidth = 0;
			_smHeight = 0;
			
			if (_frames.length == 0)
				return;
			
			var hLines:Dictionary = new Dictionary();
			var vLines:Dictionary = new Dictionary();
			
			var makeLines:Function = function(x:int, y:int):void {
				if (hLines[y]) hLines[y] += 1;
				else hLines[y] = 1;
				if (vLines[x]) vLines[x] += 1;
				else vLines[x] = 1;
			};
			
			for each (var f:IFrame in _frames)
			{
				makeLines(f.x, f.y); 
				makeLines(f.x, f.y + f.height); 
				makeLines(f.x + f.width, f.y); 
				makeLines(f.x + f.width, f.height); 
			}
			
			var xCoords:Vector.<int> = new Vector.<int>();
			var yCoords:Vector.<int> = new Vector.<int>();
			
			for (var key:String in vLines)
				xCoords.push(int(key));
			
			for (key in hLines)
				yCoords.push(int(key));
			
			xCoords.sort(Array.NUMERIC);
			yCoords.sort(Array.NUMERIC);
			
			var l:int = 0, t:int = 0, r:int = 0, b:int = 0;
			var smX:int = 0, smY:int = 0;
			
			for (var i:int = 0; i < yCoords.length; i++)
			{
				if (b >= yCoords[i])
					continue;
				
				b = yCoords[i];
				
				var row:Vector.<Space> = new Vector.<Space>;
				smX = 0;
				
				if (t >= b)
					throw Error("Space matrix calculation failed.");
				
				for (var j:int = 0; j < xCoords.length; j++)
				{
					if (r >= xCoords[j])
						continue;
					
					r = xCoords[j];
					
					if (l >= r)
						throw Error("Space matrix calculation failed.");
					
					row[smX++] = new Space(l, t, r - l, b- t);
					
					l = r;
				}
				
				t = b;
				l = 0;
				r = 0;
				
				if (smX)
					_spaceMatrix[smY++] = row;
			}
			
			_smWidth = smX;
			_smHeight = smY;
			
			for each (f in _frames)
			{
				for (i = 0; i < _spaceMatrix.length; i++)
				{
					row = _spaceMatrix[i];
					for (j = 0; j < row.length; j++)
					{
						var s:Space = row[j];
						if (s && s.belongs(f))
							row[j] = null;
					}
				}
			}
		}
	}
}